package net.tyw.web.servlet;

import net.tyw.common.encry.Token;
import net.tyw.common.utils.ValidateUtil;
import net.tyw.core.auth.OauthConstants;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created on 2015年09月24
 * <p>
 * Title: Servlet上下文
 * </p>
 * <p>
 * Description: 获取请求上下文的request,response等信息
 * </p>
 * <p>
 * Copyright: Copyright (c) 2015
 * </p>
 *
 * @author jtoms
 * @version 1.0
 */
public class ServletContext {
    /**
     * <p>
     * Web 服务器反向代理中用于存放客户端原始 IP 地址的 Http header 名字，若新增其他的需要修改其中的值。
     * </p>
     */
    private final static String[] PROXY_REMOTE_IP_ADDRESS = {"X-Forwarded-For", "X-Real-IP"};

    private final static ThreadLocal<ServletContext> controllerContext = new ThreadLocal<>();

    private final static String REQUEST = "____REQUEST____";

    private final static String RESPONSE = "____RESPONSE____";

    private final static String IS_MOBILE = "____IS_MOBILE____";

    private final static String SESSION_ID = "____SESSION_ID____";

    private final static String REMOTE_IP = "____REMOTE_IP____";

    private Map<String, Object> context;

    private ServletContext(Map<String, Object> context) {
        this.context = context;
    }

    /**
     * Created on 2015-09-24
     * <p>
     * Description:初始化 该方法在ControllerFilter中调用其他地方不能调用
     * </p>
     */
    public static void instance(HttpServletRequest request, HttpServletResponse response) {
        controllerContext.set(new ServletContext(new HashMap<String, Object>(5)));
        controllerContext.get().getContext().put(REQUEST, request);
        controllerContext.get().getContext().put(RESPONSE, response);
        controllerContext.get().getContext().put(IS_MOBILE, isMobile(request));

        String sessionId = request.getHeader(OauthConstants.OAUTH_HEADER_SESSION_NAME);
        controllerContext.get().getContext().put(SESSION_ID, Token.checkToken(sessionId) ? sessionId : null);

        controllerContext.get().getContext().put(REMOTE_IP, getRemoteIp(request));
    }

    public static void clean() {
        controllerContext.remove();
        controllerContext.set(null);
    }

    /**
     * Created on 2015-09-24
     * <p>
     * Description:获取 request
     * </p>
     */
    public static HttpServletRequest getRequest() {
        return (HttpServletRequest) controllerContext.get().getContext().get(REQUEST);
    }

    /**
     * Created on 2015-09-24
     * <p>
     * Description:获取 response
     * </p>
     */
    public static HttpServletResponse getResponse() {
        return (HttpServletResponse) controllerContext.get().getContext().get(RESPONSE);
    }

    public static boolean isMobile() {
        return (Boolean) controllerContext.get().getContext().get(IS_MOBILE);
    }

    public static String getSessionId() {
        return (String) controllerContext.get().getContext().get(SESSION_ID);
    }

    public static String getRemoteIp() {
        return (String) controllerContext.get().getContext().get(REMOTE_IP);
    }

    /**
     * 判断访问来源是否为手机
     *
     * @param request
     * @return
     */
    private static boolean isMobile(HttpServletRequest request) {
        String userAgent = request.getHeader("USER-AGENT").toLowerCase();
        if (ValidateUtil.isBlank(userAgent)) {
            userAgent = "";
        }
        // 先检测几个特定的pc来源
        String[] desktopSysAgents = new String[]{"Windows NT", "compatible; MSIE 9.0;", "Macintosh"};
        for (String d : desktopSysAgents) {
            if (userAgent.toLowerCase().contains(d.toLowerCase())) {
                return false;
            }
        }
        // 在通过正则表达式检测是否手机浏览器
        String phoneReg = "\\b(ip(hone|od)|android|opera m(ob|in)i|windows (phone|ce)|blackberry"
                + "|s(ymbian|eries60|amsung)|p(laybook|alm|rofile/midp|laystation portable)|nokia|fennec|htc[-_]"
                + "|mobile|up.browser|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";
        String tableReg = "\\b(ipad|tablet|(Nexus 7)|up.browser|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";
        Pattern phonePat = Pattern.compile(phoneReg, Pattern.CASE_INSENSITIVE);
        Pattern tablePat = Pattern.compile(tableReg, Pattern.CASE_INSENSITIVE);
        // 匹配
        Matcher matcherPhone = phonePat.matcher(userAgent);
        Matcher matcherTable = tablePat.matcher(userAgent);
        if (matcherPhone.find() || matcherTable.find()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * <p>
     * 获取请求的客户端的 IP 地址。若应用服务器前端配有反向代理的 Web 服务器， 需要在 Web 服务器中将客户端原始请求的 IP 地址加入到
     * HTTP header 中。 详见 {@link ServletUtil#PROXY_REMOTE_IP_ADDRESS}
     * </p>
     *
     * @return
     */
    private static String getRemoteIp(HttpServletRequest request) {
        for (int i = 0; i < PROXY_REMOTE_IP_ADDRESS.length; i++) {
            String ip = request.getHeader(PROXY_REMOTE_IP_ADDRESS[i]);
            if (!ValidateUtil.isBlank(ip)) {
                return getRemoteIpFromForward(ip);
            }
        }
        return request.getRemoteHost();
    }

    /**
     * <p>
     * 从 HTTP Header 的 X-Forward-IP 头中截取客户端连接 IP 地址。如果经过多次反向代理， 在 X-Forward-IP
     * 中获得的是以“,&lt;SP&gt;”分隔 IP 地址链，第一段为客户端 IP 地址。
     * </p>
     *
     * @param xforwardIp
     * @return
     */
    private static String getRemoteIpFromForward(String xforwardIp) {
        int commaOffset = xforwardIp.indexOf(',');
        if (commaOffset < 0) {
            return xforwardIp;
        }
        return xforwardIp.substring(0, commaOffset);
    }

    private Map<String, Object> getContext() {
        return context;
    }
}
